clear;
%% Erstellen, Trainieren und Testen eines Feedforward Backpropagation Netzwerks

% VORSICHT! Der Durchlauf kann SEHR lange dauern (ca. 2 Stunden), im Packet ist
% eine kleinere Version der enthalten, die das
% Training skizziert, aber prinzipiell laeuft auch das hier :-).
disp('Starting neural network training and test process.');

% Trainings- und Testset erstellen
feature_count = 8; % Anzahl der verwendeten Features
train_count = 350;  % Anzahl der verwendeten Trainingsdatensaetze
[Tr Te] = create_datasets_naive(train_count);    % 100 Datensaetze Training, der rest Test

% Variablen zum Aufnehmen der trainierten Netzwerke initialisieren
networks = cell(9100,1);

%Ergebnis enthaelt folgende Angaben deren Index zu dem Cell-Array networks
%korrespondiert:
% 1.Spalte: Neuronen in der verborgenen Schicht
% 2.Spalte: Lernrate
% 3.Spalte: Momentum
% 4.Spalte: Index (1:10, pro Parametrisierung werden 10 Versuche gemacht)
% 5.Spalte: # richtig klassifizierter Elemente des Testsets
% 6.Spalte: Sum of Squared Error des Testsets
ergebnis = zeros(9100,6);

% best_ergebnis enthaelt zusammengefasste Informationen
% 1.Spalte: Neuronen in der verborgenen Schicht
% 2.Spalte: Lernrate
% 3.Spalte: Momentum
% 4.Spalte: # richtig klassifizierte Elemente des Besten der
% Parameterklasse
% 5.Spalte: Sum of Squared Error des Besten der Parameterklasse
% 6.Spalte: Durchschnittlicher SSE der Parameterklasse
% 7.Spalte: Durchschnittliche Klassifikationsrate in der Parameterklasse
% 8.Spalte: Standardabweichung in der Parameterklasse
best_ergebnis = zeros(910, 8);
best_networks = cell(910, 1);
% Spalte 1 = Network, Spalte 2 = Classification Rate, Spalte 

test_count = size(Te, 1); % Anzahl der Objekte in der Eingabemenge
dim = size(Te, 2) - 1; % Anzahl der Dimensionen pro Objekt

% Set up Test and Training Data
Tr_Target = transpose(Tr(:, dim+1));
Tr = transpose(Tr(:, 1:dim));

Te_Target = transpose(Te(:, dim+1));
Te = transpose(Te(:, 1:dim));

% Initialisierung und Normalisierung der Parameter
TF = {'tansig' 'tansig'};
counter = 1;

% Messen der Trainingszeit
trainAllStart=tic; 

for neuron = 5:30 % Schleife laeft durch die Anzahl von Neuronen im hidden layer
    for lr = 0.2:0.1:0.8 % Schleife versucht unterschiedliche Lernraten
        for mom = 0.3:0.1:0.7 % Schleife versucht unterschiedle Momenti
            for i = 1:10 % Schleife gibt jeder Konfiguration 10 Chancen
                networks{counter, 1} = neural_train(Tr, Tr_Target, neuron, TF, lr, mom);
                Parameter Speichern
                ergebnis(counter, 1) = neuron;
                ergebnis(counter, 2) = lr;
                ergebnis(counter, 3) = mom;
                ergebnis(counter, 4) = i; 
                counter = counter + 1; % Counter erhoehen
            end
        end
        disp([num2str(counter) ' networks of 9100 trained ...']);
    end
end

trainAllElapsed=toc(trainAllStart);

%save ('networks.mat', 'networks');
disp(['Finished network training after ' num2str(trainAllElapsed) ' seconds']);

%load 'networks.mat'; % Loesch mich am Schluss
%load 'networks-param.mat'; % Loesch mich am Schluss

%% Berechnen welches Netzwerk unser Testset am besten klassifiziert

% Testen mit unseren Testdaten
% Messen der Trainingszeit
testAllStart=tic; 
for i = 1:9100
    % Aufrufen der Klassifikationsfunktion
    [classes error_rate performance] = neural_classify(networks{i,1}, Te, Te_Target);
    % Vergleichen der Klassifikation mit dem Target Vektor
    correct_results = sum(classes == Te_Target) / test_count * 100;
    ergebnis(i, 5) = correct_results;
    ergebnis(i, 6) = error_rate; % Sum of Squared Errors
end

testAllElapsed=toc(testAllStart);
disp(['Finished network test after ' num2str(testAllElapsed) ' seconds']);
save ('networks-param.mat', 'ergebnis');

%% Beste Ergebnisse speichern und aggregieren
disp('Find best results and start of evaluation');
% Immer bloecke von 10 Ergebnissen anschauen und nur das beste Netzwerk
% behalten
for j = 1:910 % Bei 9100 Netzwerken gibt es 910 Zehnerbloecke mit den selben Parametern
    if j == 1
        % Jetzt wird das Cell-Array der besten Netze der jeweiligen Klasse
        % zusammengestellt, die ersten 10 sind direkt mit dem max index
        % aufrufbar.
        [tmp tmpIX] = max ( ergebnis( 1 : 10, 5 ));
        p_avg = mean ( ergebnis( 1 : 10, 5 ));
        p_std = std ( ergebnis( 1 : 10, 5 ));
        p_avg_sse = mean ( ergebnis( 1 : 10, 6 ));
        best_ergebnis(j, 1:3) = ergebnis(tmpIX, 1:3);
        best_ergebnis(j, 4:5) = ergebnis(tmpIX, 5:6);
        best_ergebnis(j, 6) = p_avg_sse;
        best_ergebnis(j, 7) = p_avg;
        best_ergebnis(j, 8) = p_std;
        best_networks{j,1} = networks{ tmpIX, 1};
    else
        % Jetzt wird das Cell-Array der besten Netze der jeweiligen Klasse
        % zusammengestellt, der jeweilige Zehnerblock + der Position des
        % besten Netzwerks in dem Block ergibt den Fundort.
        [tmp tmpIX] = max ( ergebnis( (j-1)*10+1 : j*10, 5 ));
        p_avg = mean ( ergebnis( (j-1)*10+1 : j*10, 5 ));
        p_std = std ( ergebnis( (j-1)*10+1 : j*10, 5 ));
        p_avg_sse = mean ( ergebnis( (j-1)*10+1 : j*10, 6 ));
        best_ergebnis(j, 1:3) = ergebnis(tmpIX + (j-1)*10, 1:3);
        best_ergebnis(j, 4:5) = ergebnis(tmpIX + (j-1)*10, 5:6);
        best_ergebnis(j, 6) = p_avg_sse;
        best_ergebnis(j, 7) = p_avg;
        best_ergebnis(j, 8) = p_std;
        best_networks{j,1} = networks{ tmpIX, 1};
    end
end

save ('best_networks.mat', 'best_networks');
save ('best_ergebnis.mat', 'best_ergebnis');
disp ('Finished test and best result extraction process');

% Gewichte Anzeigen
%network.IW{1,1}
%network.LW{2,1}

%% Post-Training Analyse
[allBestClassification cr_IX] = max (best_ergebnis(:, 4));
allBestSSE = min (best_ergebnis(:, 5));
disp('=== Bestes Result ===');
disp(['Correctly classified: ' num2str(allBestClassification) '%' ]);
disp(['Sum of squared error: ' num2str(allBestSSE) ]);
disp('=== Parameters: ===');
disp(['Neurons=' num2str(best_ergebnis(cr_IX, 1)) ]);
disp(['Learn rate=' num2str(best_ergebnis(cr_IX, 2)) ]); 
disp(['Momentum=' num2str(best_ergebnis(cr_IX, 3)) ]);
